#![allow(dead_code)]

use dj::{
    custom_parsing::{self, ExprVecExt},
    Expr,
};

#[derive(Debug, Default)]
pub struct Id {
    registry_id: String,
}

impl dj::ParseFromExpr for Id {
    fn from_expr(value: Expr) -> dj::Result<Self> {
        if let Some((key, expr)) = value.into_kv_pair() {
            let id: String = match key.as_str() {
                "item-id" | "mach-id" => expr.parse_into()?,
                _ => return Err(custom_parsing::Error::UnrecognizedIdent(key))?,
            };
            return Ok(Self { registry_id: id });
        }
        Err(custom_parsing::Error::ExprMismatch { expect: "pair" })?
    }
}

#[derive(Debug, Default)]
pub struct ItemStack {
    id: Id,
    count: i32,
}

impl dj::ParseFromExpr for ItemStack {
    fn from_expr(expr: Expr) -> dj::Result<Self> {
        if let Some(mut exprs) = expr.into_combination("stack") {
            let id = exprs.pop_into::<Id>()?;
            let count = exprs.pop_into::<i32>()?;
            return Ok(Self { id, count });
        }
        Err(custom_parsing::Error::ExprMismatch { expect: "stack" })?
    }
}
#[derive(Debug, Default)]
pub struct Recipe {
    name: String,
    using_mach: Id,
    from: ItemStack,
    to: ItemStack,
}

impl dj::ParseFromExpr for Recipe {
    fn from_expr(expr: Expr) -> dj::Result<Self> {
        if let Some(exprs) = expr.into_combination("recipe") {
            let mut ret = Self::default();
            for (field, expr) in exprs.into_iter().flat_map(Expr::into_kv_pair) {
                match field.as_str() {
                    "name" => ret.name = expr.parse_into()?,
                    "using" => ret.using_mach = expr.parse_into()?,
                    "from" => ret.from = expr.parse_into()?,
                    "to" => ret.to = expr.parse_into()?,
                    _ => (),
                }
            }
            return Ok(ret);
        }
        Err(custom_parsing::Error::ExprMismatch { expect: "recipe" })?
    }
}

const DJ_DEFINITION: &str = include_str!("simple_convert.dj");

fn main() -> dj::Result<()> {
    let s: Recipe = dj::parse_to(DJ_DEFINITION)?;
    println!("{s:?}");
    Ok(())
}
